import os.path
import time

from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy.orm import Session

from config import project_dir
from database import get_db
from schema.cdn_config_schema import CdnConfigCreate, CdnConfigUpdate
from model.cdn_config import CdnConfig
from pydantic import ConfigDict
import yaml
import subprocess
import re
import socket
import paramiko

# 服务器列表
servers_list = [
    {"host": "103.118.40.245", "user": "root"}
    # 可以继续添加
]


router = APIRouter(tags=["api"])

ingress_yaml_path = f"{project_dir}/cdn_yaml/ingress.yaml"
nginx_yaml_path = f"{project_dir}/cdn_yaml/nginx.yaml"
ssl_yaml_path = f"{project_dir}/cdn_yaml/ssl.yaml"

@router.post("/add")
def register(cdn_config: CdnConfigCreate, db: Session = Depends(get_db)):
    try:
        db_cdn_config = db.query(CdnConfig).filter(CdnConfig.domain == cdn_config.domain).first()
        if db_cdn_config:
            return {"message": "CdnConfig already exists"}
        db_cdn_config = CdnConfig(
            user_id=cdn_config.userId,
            domain=cdn_config.domain,
            originIp=cdn_config.originIp,
            ifDeploy=False
        )
        db.add(db_cdn_config)
        db.commit()
        db.refresh(db_cdn_config)
        return ""
    except Exception as e:
        db.rollback()
        raise HTTPException(status_code=500, detail=str(e))

@router.put("/update/{cdn_config_id}")
def update_cdn_config(cdn_config_id: int, cdn_config: CdnConfigUpdate, db: Session = Depends(get_db)):
    try:
        db_cdn_config = db.query(CdnConfig).filter(CdnConfig.id == cdn_config_id).first()
        if not db_cdn_config:
            raise HTTPException(status_code=404, detail="CDN configuration not found")
        for key, value in cdn_config.dict().items():
            if key == 'origin':
                key = 'domain'
            if key == 'ns_ip':
                key = 'originIp'
            setattr(db_cdn_config, key, value)
        db.commit()
        db.refresh(db_cdn_config)
        return db_cdn_config
    except Exception as e:
        db.rollback()
        raise HTTPException(status_code=500, detail=str(e))

@router.delete("/delete/{cdn_config_id}")
def delete_cdn_config(cdn_config_id: int, db: Session = Depends(get_db)):
    try:
        db_cdn_config = db.query(CdnConfig).filter(CdnConfig.id == cdn_config_id).first()
        if not db_cdn_config:
            raise HTTPException(status_code=404, detail="CDN configuration not found")
        db.delete(db_cdn_config)
        db.commit()
        return db_cdn_config
    except Exception as e:
        db.rollback()
        raise HTTPException(status_code=500, detail=str(e))

@router.get("/query/{domain}")
def query_cdn_config(domain: str, db: Session = Depends(get_db)):
    try:
        db_cdn_config = db.query(CdnConfig).filter(CdnConfig.domain == domain).first()
        if not db_cdn_config:
            raise HTTPException(status_code=404, detail="CDN configuration not found")
        return db_cdn_config
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))

# @router.get("/query_all")
# def query_all_cdn_configs(db: Session = Depends(get_db)):
#     try:
#         db_cdn_configs = db.query(CdnConfig).all()
#         return db_cdn_configs
#     except Exception as e:
#         raise HTTPException(status_code=500, detail=str(e))


@router.get("/query_all")
def query_all_cdn_configs(db: Session = Depends(get_db)):
    try:
        db_cdn_configs = db.query(CdnConfig).all()
        results = []

        for config in db_cdn_configs:
            record = {
                "id": config.id,
                "domain": config.domain,
                "originIp": config.originIp,
                "ifDeploy": config.ifDeploy
            }

            # 遍历服务器，获取证书内容（如果有）
            cert_fullchain = None
            cert_privkey = None
            for server in servers_list:
                exists, fullchain, privkey = check_cert(server['host'], server['user'], config.domain)
                if exists:
                    cert_fullchain = fullchain
                    cert_privkey = privkey
                    break  # 找到第一个存在证书的服务器即可

            record["fullchain"] = cert_fullchain
            record["privkey"] = cert_privkey
            results.append(record)

        return results

    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))



def _update_hosts(new_host:str):
    # 读取 ingress.yaml
    with open(ingress_yaml_path, "r", encoding="utf-8") as f:
        docs = list(yaml.safe_load_all(f))  # 支持多文档

    for data in docs:
        if "spec" in data:
            # 修改 rules 下所有 host
            if "rules" in data["spec"]:
                for rule in data["spec"]["rules"]:
                    if "host" in rule:
                        rule["host"] = new_host

            # 修改 tls.hosts
            if "tls" in data["spec"]:
                for tls in data["spec"]["tls"]:
                    if "hosts" in tls:
                        tls["hosts"] = [new_host]

    # 写回文件
    with open(ingress_yaml_path, "w", encoding="utf-8") as f:
        yaml.dump_all(docs, f, sort_keys=False, allow_unicode=True)

    print(f"✅ 已完成 host 替换为 {new_host}，保存到 {ingress_yaml_path}")

def _update_nginx(new_proxy: str):
    # 读取 YAML
    print(new_proxy)
    with open(nginx_yaml_path, "r", encoding="utf-8") as f:
        content = f.read()

    # 替换 proxy_pass 地址
    updated_content = re.sub(r"proxy_pass\s+\S+", f"proxy_pass {new_proxy};", content)

    # 写入新文件
    with open(nginx_yaml_path, "w", encoding="utf-8") as f:
        f.write(updated_content)

    print(f"✅ 已完成替换，结果保存到 {nginx_yaml_path}")

def _update_ssl(new_host:str):
    # 读取 YAML
    with open(ssl_yaml_path, "r", encoding="utf-8") as f:
        content = f.read()

    # 替换 dnsNames 下的所有域名为 localhost
    # 匹配 dnsNames: 后的缩进行（以空格开头的行），替换为 localhost
    updated_content = re.sub(
        r"(dnsNames:\n\s+-\s+)[^\n]+",
        lambda m: f"{m.group(1)}{new_host}",
        content
    )

    # 写入新文件
    with open(ssl_yaml_path, "w", encoding="utf-8") as f:
        f.write(updated_content)

    print(f"✅ 已完成替换，结果保存到 {updated_content}")


def _del_deployment(yaml_path: str):
    """应用单个 YAML 文件，并打印 stdout/stderr"""
    result = subprocess.run(
        ["kubectl", "delete", "-f", yaml_path],
        capture_output=True,
        text=True
    )
    print(f"Applying {yaml_path}:")
    print("stdout:", result.stdout)
    print("stderr:", result.stderr)

    if result.returncode != 0:
        raise RuntimeError(f"Failed to apply {yaml_path}")

    return result

def _apply_yaml(yaml_path: str):
    """应用单个 YAML 文件，并打印 stdout/stderr"""
    result = subprocess.run(
        ["kubectl", "apply", "-f", yaml_path],
        capture_output=True,
        text=True
    )
    print(f"Applying {yaml_path}:")
    print("stdout:", result.stdout)
    print("stderr:", result.stderr)

    if result.returncode != 0:
        raise RuntimeError(f"Failed to apply {yaml_path}")

    return result


def _restart():
    yaml_files = [nginx_yaml_path, ingress_yaml_path, ssl_yaml_path]

    for yaml_path in yaml_files:
        _del_deployment(yaml_path)
        _apply_yaml(yaml_path)


def get_all_ips():
    """获取本机所有非回环、非容器虚拟IP的真实IP地址"""
    ips = []
    hostname = socket.gethostname()

    # 需要排除的IP段（容器/虚拟网络）
    EXCLUDED_NETS = [
        '127.',  # IPv4回环
        '::1',  # IPv6回环
        '172.16.',  # Docker默认网段
        '172.17.',  # Docker默认网段
        '172.18.',  # Docker自定义可能使用的网段
        '172.19.',  # Docker自定义可能使用的网段
        '172.20.',  # Docker自定义可能使用的网段
        '172.21.',  # Docker自定义可能使用的网段
        '172.22.',  # Docker自定义可能使用的网段
        '172.23.',  # Docker自定义可能使用的网段
        '172.24.',  # Docker自定义可能使用的网段
        '172.25.',  # Docker自定义可能使用的网段
        '172.26.',  # Docker自定义可能使用的网段
        '172.27.',  # Docker自定义可能使用的网段
        '172.28.',  # Docker自定义可能使用的网段
        '172.29.',  # Docker自定义可能使用的网段
        '172.30.',  # Docker自定义可能使用的网段
        '172.31.',  # Docker自定义可能使用的网段
        '192.168.',  # 其他可能的内部网络
        '10.',  # 大型内网
        '169.254.',  # 链路本地地址
        'fc00::',  # IPv6私有网络
        'fe80::'  # IPv6链路本地
    ]

    try:
        for addr in socket.getaddrinfo(hostname, None):
            ip = addr[4][0]

            # 检查是否在排除列表中
            if not any(ip.startswith(net) for net in EXCLUDED_NETS):
                if ip not in ips:
                    ips.append(ip)

        return ips if ips else ["No valid IP found"]
    except Exception as e:
        return [f"Error: {str(e)}"]


def _verify_domain(domain: str):
    try:
        ip = socket.gethostbyname(domain)
        server_ips = get_all_ips()  # 假设这是获取服务器所有IP的函数

        if ip in server_ips:
            return {
                "status": "success",
                "valid": True,
                "message": f"域名 {domain} 的A记录({ip})指向当前服务器"
            }
        else:
            return {
                "status": "success",
                "valid": False,
                "message": f"域名 {domain} 的A记录({ip})未指向当前服务器(当前服务器IP: {server_ips})"
            }
    except socket.gaierror as e:
        return {
            "status": "error",
            "valid": False,
            "message": f"域名解析失败: {str(e)}"
        }


def _update_core_dns(core_file, zone_file, zone_file_name):
    os.makedirs("/etc/coredns/zones", exist_ok=True)

    corefile_path = "/etc/coredns/Corefile"
    zonefile_path = f"/etc/coredns/zones/{zone_file_name}"

    # 写入 Corefile
    with open(corefile_path, "w") as f:
        f.write(core_file)

    # 写入 zone 文件
    with open(zonefile_path, "w") as f:
        f.write(zone_file)

    print("Corefile 和 zone 文件已写入完成！")
    # 重启 CoreDNS 服务
    try:
        subprocess.run(["systemctl", "restart", "coredns"], check=True)
        print("CoreDNS 服务已重启！")
    except subprocess.CalledProcessError as e:
        print("重启 CoreDNS 服务失败:", e)


def deploy_to_server(host, user, nginx_conf_path, nginx_conf_content):
    ssh = paramiko.SSHClient()
    ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    ssh.connect(hostname=host, username=user)  # 免密 SSH

    # 上传 nginx 配置
    sftp = ssh.open_sftp()
    remote_path = f"/etc/nginx/conf.d/{os.path.basename(nginx_conf_path)}"
    with sftp.file(remote_path, 'w') as f:
        f.write(nginx_conf_content)
    sftp.close()

    # 重载 nginx
    stdin, stdout, stderr = ssh.exec_command("nginx -s reload")
    out = stdout.read().decode()
    err = stderr.read().decode()
    ssh.close()
    return out, err


def check_cert(host: str, user: str, domain: str):
    """
    核查远程服务器是否申请了证书
    返回：
      - 如果存在: (True, fullchain_content, privkey_content)
      - 如果不存在: (False, None, None)
    """
    base_path = f"/etc/letsencrypt/live/{domain}"
    fullchain_path = f"{base_path}/fullchain.pem"
    privkey_path = f"{base_path}/privkey.pem"

    client = paramiko.SSHClient()
    client.set_missing_host_key_policy(paramiko.AutoAddPolicy())

    try:
        # SSH免密登录
        client.connect(hostname=host, username=user)

        # 检查文件是否存在
        check_cmd = f'test -f {fullchain_path} && test -f {privkey_path} && echo "YES" || echo "NO"'
        stdin, stdout, stderr = client.exec_command(check_cmd)
        exists = stdout.read().decode().strip() == "YES"

        if not exists:
            return False, None, None

        # 读取证书内容
        stdin, stdout, stderr = client.exec_command(f'cat {fullchain_path}')
        fullchain_content = stdout.read().decode()

        stdin, stdout, stderr = client.exec_command(f'cat {privkey_path}')
        privkey_content = stdout.read().decode()

        return True, fullchain_content, privkey_content

    finally:
        client.close()



@router.put("/deploy/{cdn_config_id}")
def deploy_cdn_config(cdn_config_id: int, db: Session = Depends(get_db)):
    db_cdn_config = db.query(CdnConfig).filter(CdnConfig.id == cdn_config_id).first()
    if not db_cdn_config:
        raise HTTPException(status_code=404, detail="CDN configuration not found")

    # 构建 nginx 配置
    with open(os.path.join("template/nginx.conf"), encoding="utf8") as f:
        nginx_tmp = f.read()

    nginx_conf = nginx_tmp.replace("<dns-name>", db_cdn_config.domain).replace('<source-ip>', db_cdn_config.originIp)
    nginx_conf_path = f"{db_cdn_config.domain}.conf"

    results = []
    for server in servers_list:
        # 部署 nginx 配置到服务器
        out, err = deploy_to_server(
            host=server['host'],
            user=server['user'],
            nginx_conf_path=nginx_conf_path,
            nginx_conf_content=nginx_conf
        )

        # 检查远程证书
        exists, fullchain, privkey = check_cert(server['host'], server['user'], db_cdn_config.domain)

        results.append({
            "host": server['host'],
            "nginx_out": out,
            "nginx_err": err,
            "cert_exists": exists,
            "fullchain": fullchain if exists else None,
            "privkey": privkey if exists else None
        })

    return {
        "status": "success",
        "message": "CDN配置已部署到所有服务器",
        "results": results,
        "cdn_config": {
            "id": db_cdn_config.id,
            "is_deployed": db_cdn_config.ifDeploy
        }
    }



